import json
import random
import string
import sys
import traceback
from datetime import datetime
from decimal import *

import math
import requests
from dotenv import dotenv_values
from sqlalchemy import and_

from applications.extensions import db


class ModelFilter:
    """
    orm多参数构造器
    """
    filter_field = {}
    filter_list = []

    type_exact = "exact"
    type_neq = "neq"
    type_greater = "greater"
    type_less = "less"
    type_vague = "vague"
    type_contains = "contains"
    type_between = "between"

    def __init__(self):
        self.filter_field = {}
        self.filter_list = []

    def exact(self, field_name, value):
        """
        准确查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": value, "type": self.type_exact}

    def neq(self, field_name, value):
        """
        不等于查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": value, "type": self.type_neq}

    def greater(self, field_name, value):
        """
        大于查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": value, "type": self.type_greater}

    def less(self, field_name, value):
        """
        大于查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": value, "type": self.type_less}

    def vague(self, field_name, value: str):
        """
        模糊查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": ('%' + value + '%'), "type": self.type_vague}

    def left_vague(self, field_name, value: str):
        """
        左模糊查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": ('%' + value), "type": self.type_vague}

    def right_vague(self, field_name, value: str):
        """
        左模糊查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": (value + '%'), "type": self.type_vague}

    def contains(self, field_name, value: str):
        """
        包含查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value and value != '':
            self.filter_field[field_name] = {"data": value, "type": self.type_contains}

    def between(self, field_name, value1, value2):
        """
        范围查询字段
        :param field_name: 模型字段名称
        :param value: 值
        """
        if value1 and value2 and value1 != '' and value2 != '':
            self.filter_field[field_name] = {"data": [value1, value2], "type": self.type_between}

    def get_filter(self, model: db.Model):
        """
        获取过滤条件
        :param model: 模型字段名称
        """
        for k, v in self.filter_field.items():
            if v.get("type") == self.type_vague:
                self.filter_list.append(getattr(model, k).like(v.get("data")))
            if v.get("type") == self.type_contains:
                self.filter_list.append(getattr(model, k).contains(v.get("data")))
            if v.get("type") == self.type_exact:
                self.filter_list.append(getattr(model, k) == v.get("data"))
            if v.get("type") == self.type_neq:
                self.filter_list.append(getattr(model, k) != v.get("data"))
            if v.get("type") == self.type_greater:
                self.filter_list.append(getattr(model, k) > v.get("data"))
            if v.get("type") == self.type_less:
                self.filter_list.append(getattr(model, k) < v.get("data"))
            if v.get("type") == self.type_between:
                self.filter_list.append(getattr(model, k).between(v.get("data")[0], v.get("data")[1]))
        return and_(*self.filter_list)


class Tools:
    import datetime
    from flask import session
    from applications.models import Hospital, Equipment

    def day_get(self, d=None):
        if not d:
            d = self.datetime.datetime.now()
        """
        获取前7天日期(包含当天)
        """
        # 通过for 循环得到天数，如果想得到两周的时间，只需要把8改成15就可以了。
        for i in range(0, 7):
            oneday = self.datetime.timedelta(days=i)
            day = d - oneday
            date_to = self.datetime.datetime(day.year, day.month, day.day)
            yield str(date_to)[0:10]

    @staticmethod
    def random_string_generator(str_size, allowed_chars=None):
        """
        获取随机字符串
        :param str_size: 字符串长度
        :param allowed_chars: 字符串值的范围
        """
        if allowed_chars is None:
            # 默认是所有数字+所有大写字母
            allowed_chars = string.digits + string.ascii_uppercase
        return ''.join(random.choice(allowed_chars) for x in range(str_size))

    @staticmethod
    def find_duplicates(lst):
        return [x for x in lst if lst.count(x) > 1]

    def get_hospital_list(self):
        """
        获取医院列表
        """
        # 超级管理员可以查询所有的数据
        if 3 in self.session.get('role')[0]:
            return self.Hospital.query.filter_by(hospital_no=self.session.get("hospital_no"), enable=1).all()
        else:
            return self.Hospital.query.filter_by(enable=1).all()

    def get_device_list(self):
        """
        获取设备列表
        """
        # 超级管理员可以查询所有的数据
        if 3 in self.session.get('role')[0]:
            return self.Equipment.query.filter_by(enable=1).all()
        else:
            return self.Equipment.query.filter_by(enable=1).all()


# 数据计算
def calculated(data):
    if 'u6' not in data:
        return None
    if 'mir' not in data:
        return None
    u6 = data['u6']
    mir = data['mir']
    x = []
    for i in range(len(u6)):
        x.append(Decimal(mir[i]['ct']) - Decimal(u6[i]['ct']))
    y = Decimal('-0.474') * x[0] - Decimal('0.55') * x[1] - Decimal('0.338') * x[2] - Decimal('0.741') * x[3] - Decimal(
        '6.586')
    re = {'x': x, 'dx': math.exp(y) / (1 + math.exp(y)), 'y': y}
    return re


# 错误处理
def err():
    exc_type, exc_value, exc_traceback = sys.exc_info()

    for filename, linenum, funcname, source in traceback.extract_tb(exc_traceback):
        err = 'File %s, line %s, in %s, %s' % (filename, str(linenum), funcname, source)
        time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        log_path = 'logs/err.log'
        with open(log_path, 'a') as f:
            err_info = 'exc_type: %s\nexc_value: %s\nerr_info: %s\ntime: %s\n' % (
                str(exc_type), str(exc_value), err, time)
            f.write(err_info)


# post提交json代码
def json_post(url, json):
    config = dotenv_values('.flaskenv')
    headers = {
        'SECRET-KEY': config.get('SECRET_KEY'),
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE AZYC'
    }
    r = requests.post(url=url, json=json, headers=headers)
    return r.text


class Excel:
    import io
    import xlsxwriter
    from applications.models import Record

    def record_out_excel(self, r):
        output = self.io.BytesIO()
        workbook = self.xlsxwriter.Workbook(output, {'strings_to_numbers': True})
        sheet = workbook.add_worksheet('sheet1')
        record = self.Record.query.filter(self.Record.random.in_(r)).all()
        # #  设置列宽
        sheet.set_column('A:A', 20)
        sheet.set_column('B:B', 10)
        sheet.set_column('C:C', 8)
        sheet.set_column('D:D', 15)
        sheet.set_column('E:E', 10)
        sheet.set_column('F:F', 10)
        sheet.set_column('G:G', 10)
        sheet.set_column('H:H', 15)
        sheet.set_column('I:I', 10)
        sheet.set_column('G:G', 10)
        sheet.set_column('K:K', 10)
        sheet.set_column('L:L', 15)
        sheet.set_column('M:M', 10)
        sheet.set_column('N:N', 10)
        sheet.set_column('O:O', 10)
        sheet.set_column('P:P', 15)
        sheet.set_column('Q:Q', 10)
        sheet.set_column('R:R', 10)
        sheet.set_column('S:S', 10)
        sheet.set_column('T:T', 15)
        sheet.set_column('U:U', 15)

        # 设置表头
        header_format = workbook.add_format(
            {'bold': True, 'font_size': 14, 'align': 'center', 'valign': 'vcenter', 'border': 1})
        title = ['日期', '样本号', '浓度', 'miRNA132 Ct', 'U6 Ct', '△Ct', '平均值', 'miRNA30 Ct', 'U6 Ct', '△Ct',
                 '平均值', 'miRNA24 Ct', 'U6 Ct', '△Ct', '平均值', 'miRNA23 Ct', 'U6 Ct', '△Ct', '平均值', 'y', 'Dx']
        time_format = workbook.add_format({'num_format': 'yyyy-m-d h:mm'})
        for index in range(len(title)):  # 开始循环
            sheet.write(0, index, title[index], header_format)
        i = 1
        for p in record:
            if p.cts == '' or p.cts is None:
                continue
            sheet.write_datetime(i, 0, p.test_time, time_format)
            sheet.write(i, 1, p.name)
            sheet.write(i, 2, '')
            obj = json.loads(p.cts)
            mir = obj['mir']
            u6 = obj['u6']
            r = calculated(obj)
            sheet.write(i, 3, mir[0]['ct'])
            sheet.write(i, 4, u6[0]['ct'])
            sheet.write(i, 5, r['x'][0])
            sheet.write(i, 6, r['x'][0])

            sheet.write(i, 7, mir[1]['ct'])
            sheet.write(i, 8, u6[1]['ct'])
            sheet.write(i, 9, r['x'][1])
            sheet.write(i, 10, r['x'][1])

            sheet.write(i, 11, mir[2]['ct'])
            sheet.write(i, 12, u6[2]['ct'])
            sheet.write(i, 13, r['x'][2])
            sheet.write(i, 14, r['x'][2])

            sheet.write(i, 15, mir[3]['ct'])
            sheet.write(i, 16, u6[3]['ct'])
            sheet.write(i, 17, r['x'][3])
            sheet.write(i, 18, r['x'][3])
            sheet.write(i, 19, r['y'])
            sheet.write(i, 20, r['dx'])

            del r
            del obj
            del mir
            del u6
            i += 1
        workbook.close()  # 需要关闭
        output.seek(0)
        return output


class IdcardValidator:
    import datetime  # 导入时间
    area = {
        '11': '北京市', '12': '天津市', '13': '河北省', '14': '山西省', '15': '内蒙古自治区',
        '21': '辽宁省', '22': '吉林省', '23': '黑龙江省',
        '31': '上海市', '32': '江苏省', '33': '浙江省', '34': '安徽省', '35': '福建省', '36': '江西省', '37': '山东省',
        '41': '河南省', '42': '湖北省', '43': '湖南省',
        '44': '广东省', '45': '广西壮族自治区', '46': '海南省',
        '50': '重庆市', '51': '四川省', '52': '贵州省', '53': '云南省', '54': '西藏自治区',
        '61': '陕西省', '62': '甘肃省', '63': '青海省', '64': '宁夏回族自治区', '65': '新疆维吾尔族自治区',
        '81': '香港特别行政区', '82': '澳门特别行政区', '83': '台湾地区'
    }  # 全国省的字典,全局变量
    factorArr = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]  # 17位相乘系数数组
    judgeLastNumArr = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]  # %11对应的余数得到的校验码，下标为余数

    def __init__(self, number):  # 一个参数，构造函数
        self.number = str(number)  # 参数为身份证号码数组
        # self.n_flag=[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]#17位相乘数组

    def getLocation(self):  # 地区切片
        if self.isTruth() is False:
            return False
        loc = self.number[0:2]  # 前2位
        return self.area[loc]

    def getSex(self):  # 性别
        if self.isTruth() is False:
            return False
        sex = self.number[-2]  # 倒数第二位
        sex = int(sex)
        if sex % 2:
            return '男'  # 男
        return '女'  # 女

    def getAge(self):
        if self.isTruth() is False:
            return False
        birthday = self.number[6:14]  # 出生年月日

        birthYear = birthday[0:4]  # 前四位
        birthMonth = birthday[4:6]
        birthDate = birthday[6:8]
        now = self.datetime.datetime.now()
        diffYear = now.year - int(birthYear)  # int换算
        diffMonth = now.month - int(birthMonth)
        diffDay = now.day - int(birthDate)
        if diffMonth < 0 or diffMonth == 0 and diffDay < 0:
            return diffYear - 1
        return diffYear

    def isTruth(self):  # 辨别真假
        if len(self.number) is not 18:
            return False
        last = self.number[-1]  # 最后一位
        numArr = list(map(int, self.number))  # map转化数组
        numArr.pop()  # 删除最后一个校验位
        tupleFlag = list(zip(self.factorArr, numArr))  # 合成元组
        sum = 0  # 记录相乘之后的和
        for p in tupleFlag:
            # 得出一组数
            each = 1  # 系数为1与他们相乘
            for q in p:
                each = q * each
            sum += each  # 记录每组数的乘积和
        result = sum % 11  # 对11取余数
        flag = self.judgeLastNumArr[result]
        if flag is int(last):
            return True
        return False
